#include "StdAfx.h"
#include "Texture.h"
#include "Render.h"
#include "Drawlist.h"
#include "MatrixStack.h"

// use shader for font?
// defined: use a shader program
// undefined: use fixed-function
#define FONT_USE_SHADER

static const int FIRST_CHARACTER = '\x00';
static const int LAST_CHARACTER  = '\x7F';

Rect<float> sDefaultFontUVs[LAST_CHARACTER-FIRST_CHARACTER+1];

GLuint sDefaultFontHandle;

// default font identifier
static const unsigned int aDefaultFontId = 0x7bd2c61f /* "defaultfont" */;

// modified Atari 8-bit font
// TO DO: support other fonts
static const int aTextureComponents = 1;
static const size_t aTextureWidth = 128;
static const size_t aTextureHeight = 64;
static const unsigned char aTextureData[] = 
{
	0x00, 0x18, 0xc0, 0x18, 0x18, 0x00, 0xc0, 0x03, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0xff, 0x00, 0x00, 
	0x6c, 0x18, 0xc0, 0x18, 0x18, 0x00, 0xe0, 0x07, 0xc0, 0x00, 0x03, 0xf0, 0x0f, 0xff, 0x00, 0x00, 
	0xfe, 0x18, 0xc0, 0x18, 0x18, 0x00, 0x70, 0x0e, 0xe0, 0x00, 0x07, 0xf0, 0x0f, 0x00, 0x00, 0x00, 
	0xfe, 0xf8, 0xc0, 0x1f, 0x1f, 0x1f, 0x38, 0x1c, 0xf0, 0x00, 0x0f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 
	0x7c, 0xf8, 0xc0, 0x1f, 0x1f, 0x1f, 0x1c, 0x38, 0xf8, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x0f, 
	0x38, 0x18, 0xc0, 0x00, 0x18, 0x18, 0x0e, 0x70, 0xfc, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x0f, 
	0x10, 0x18, 0xc0, 0x00, 0x18, 0x18, 0x07, 0xe0, 0xfe, 0xf0, 0x7f, 0x00, 0x00, 0x00, 0xff, 0x0f, 
	0x00, 0x18, 0xc0, 0x00, 0x18, 0x18, 0x03, 0xc0, 0xff, 0xf0, 0xff, 0x00, 0x00, 0x00, 0xff, 0x0f, 
	0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x03, 0x00, 0x18, 0x0f, 0x18, 0x1e, 0x00, 0x00, 0x00, 0x00, 
	0x38, 0x00, 0x00, 0x18, 0x00, 0x00, 0x03, 0x00, 0x18, 0x0f, 0x18, 0x06, 0x18, 0x18, 0x18, 0x18, 
	0x38, 0x00, 0x00, 0x18, 0x3c, 0x00, 0x03, 0x00, 0x18, 0x0f, 0x18, 0x1e, 0x3c, 0x18, 0x0c, 0x30, 
	0xee, 0xf8, 0xff, 0xff, 0x7e, 0x00, 0x03, 0xff, 0xff, 0x0f, 0xf8, 0x06, 0x7e, 0x18, 0x7e, 0x7e, 
	0xee, 0xf8, 0xff, 0xff, 0x7e, 0xff, 0x03, 0xff, 0xff, 0x0f, 0xf8, 0x7e, 0x18, 0x7e, 0x0c, 0x30, 
	0x10, 0x18, 0x00, 0x18, 0x7e, 0xff, 0x03, 0x18, 0x00, 0x0f, 0x00, 0x18, 0x18, 0x3c, 0x18, 0x18, 
	0x38, 0x18, 0x00, 0x18, 0x3c, 0xff, 0x03, 0x18, 0x00, 0x0f, 0x00, 0x78, 0x18, 0x18, 0x00, 0x00, 
	0x00, 0x18, 0x00, 0x18, 0x00, 0xff, 0x03, 0x18, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x18, 0x66, 0x66, 0x7c, 0x66, 0x6c, 0x18, 0x70, 0x0e, 0x66, 0x18, 0x00, 0x00, 0x00, 0x60, 
	0x00, 0x18, 0x66, 0xff, 0x06, 0x36, 0x38, 0x18, 0x38, 0x1c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x30, 
	0x00, 0x18, 0x66, 0x66, 0x3c, 0x18, 0x1c, 0x18, 0x18, 0x18, 0xff, 0x7e, 0x00, 0x7e, 0x00, 0x18, 
	0x00, 0x18, 0x00, 0x66, 0x60, 0x0c, 0xf6, 0x00, 0x18, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x0c, 
	0x00, 0x00, 0x00, 0xff, 0x3e, 0x66, 0x66, 0x00, 0x38, 0x1c, 0x66, 0x18, 0x18, 0x00, 0x18, 0x06, 
	0x00, 0x18, 0x00, 0x66, 0x18, 0x62, 0xdc, 0x00, 0x70, 0x0e, 0x00, 0x00, 0x18, 0x00, 0x18, 0x02, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x06, 0x00, 
	0x3c, 0x18, 0x3c, 0x7e, 0x30, 0x7e, 0x3c, 0x7e, 0x3c, 0x3c, 0x00, 0x00, 0x30, 0x00, 0x0c, 0x3c, 
	0x66, 0x1c, 0x66, 0x30, 0x38, 0x06, 0x06, 0x60, 0x66, 0x66, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x66, 
	0x76, 0x18, 0x30, 0x18, 0x3c, 0x3e, 0x3e, 0x30, 0x3c, 0x7c, 0x18, 0x18, 0x0c, 0x00, 0x30, 0x30, 
	0x6e, 0x18, 0x18, 0x30, 0x36, 0x60, 0x66, 0x18, 0x66, 0x60, 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 
	0x66, 0x18, 0x0c, 0x66, 0x7e, 0x66, 0x66, 0x0c, 0x66, 0x30, 0x18, 0x18, 0x30, 0x7e, 0x0c, 0x00, 
	0x3c, 0x7e, 0x7e, 0x3c, 0x30, 0x3c, 0x3c, 0x0c, 0x3c, 0x1c, 0x18, 0x18, 0x60, 0x00, 0x06, 0x18, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x3c, 0x18, 0x3e, 0x3c, 0x1e, 0x7e, 0x7e, 0x7c, 0x66, 0x7e, 0x70, 0x66, 0x06, 0xc6, 0x66, 0x3c, 
	0x66, 0x3c, 0x66, 0x66, 0x36, 0x06, 0x06, 0x06, 0x66, 0x18, 0x60, 0x36, 0x06, 0xee, 0x6e, 0x66, 
	0x76, 0x66, 0x3e, 0x06, 0x66, 0x3e, 0x3e, 0x06, 0x7e, 0x18, 0x60, 0x1e, 0x06, 0xfe, 0x7e, 0x66, 
	0x76, 0x66, 0x66, 0x06, 0x66, 0x06, 0x06, 0x76, 0x66, 0x18, 0x60, 0x1e, 0x06, 0xd6, 0x7e, 0x66, 
	0x06, 0x7e, 0x66, 0x66, 0x36, 0x06, 0x06, 0x66, 0x66, 0x18, 0x66, 0x36, 0x06, 0xc6, 0x76, 0x66, 
	0x7c, 0x66, 0x3e, 0x3c, 0x1e, 0x7e, 0x06, 0x7c, 0x66, 0x7e, 0x3c, 0x66, 0x7e, 0xc6, 0x66, 0x3c, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x3e, 0x3c, 0x3e, 0x3c, 0x7e, 0x66, 0x66, 0xc6, 0x66, 0x66, 0x7e, 0x78, 0x02, 0x1e, 0x10, 0x00, 
	0x66, 0x66, 0x66, 0x06, 0x18, 0x66, 0x66, 0xc6, 0x66, 0x66, 0x30, 0x18, 0x06, 0x18, 0x38, 0x00, 
	0x66, 0x66, 0x66, 0x3c, 0x18, 0x66, 0x66, 0xd6, 0x3c, 0x3c, 0x18, 0x18, 0x0c, 0x18, 0x6c, 0x00, 
	0x3e, 0x66, 0x3e, 0x60, 0x18, 0x66, 0x66, 0xfe, 0x3c, 0x18, 0x0c, 0x18, 0x18, 0x18, 0xc6, 0x00, 
	0x06, 0x36, 0x36, 0x60, 0x18, 0x66, 0x3c, 0xee, 0x66, 0x18, 0x06, 0x18, 0x30, 0x18, 0x00, 0x00, 
	0x06, 0x6c, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0xc6, 0x66, 0x18, 0x7e, 0x78, 0x60, 0x1e, 0x00, 0xff, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x18, 0x00, 0x06, 0x00, 0x60, 0x00, 0x70, 0x00, 0x06, 0x18, 0x60, 0x06, 0x1c, 0x00, 0x00, 0x00, 
	0x3c, 0x3c, 0x06, 0x3c, 0x60, 0x3c, 0x18, 0x7c, 0x06, 0x00, 0x00, 0x06, 0x18, 0x66, 0x3e, 0x3c, 
	0x7e, 0x60, 0x3e, 0x06, 0x7c, 0x66, 0x7c, 0x66, 0x3e, 0x1c, 0x60, 0x36, 0x18, 0xfe, 0x66, 0x66, 
	0x7e, 0x7c, 0x66, 0x06, 0x66, 0x7e, 0x18, 0x66, 0x66, 0x18, 0x60, 0x1e, 0x18, 0xfe, 0x66, 0x66, 
	0x3c, 0x66, 0x66, 0x06, 0x66, 0x06, 0x18, 0x7c, 0x66, 0x18, 0x60, 0x36, 0x18, 0xd6, 0x66, 0x66, 
	0x18, 0x7c, 0x3e, 0x3c, 0x7c, 0x3c, 0x18, 0x60, 0x66, 0x3c, 0x60, 0x66, 0x3c, 0xc6, 0x66, 0x3c, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x10, 0x08, 
	0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x18, 0x1e, 0x18, 0x18, 
	0x3e, 0x7c, 0x3e, 0x7c, 0x7e, 0x66, 0x66, 0xc6, 0x66, 0x66, 0x7e, 0x18, 0x18, 0x18, 0x1c, 0x38, 
	0x66, 0x66, 0x66, 0x06, 0x18, 0x66, 0x66, 0xd6, 0x3c, 0x66, 0x30, 0x1c, 0x18, 0x38, 0x1e, 0x78, 
	0x66, 0x66, 0x06, 0x3c, 0x18, 0x66, 0x66, 0xfe, 0x18, 0x66, 0x18, 0x1c, 0x18, 0x38, 0x1c, 0x38, 
	0x3e, 0x7c, 0x06, 0x60, 0x18, 0x66, 0x3c, 0x7c, 0x3c, 0x7c, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x18, 
	0x06, 0x60, 0x06, 0x3e, 0x70, 0x7c, 0x18, 0x6c, 0x66, 0x30, 0x7e, 0x78, 0x18, 0x1e, 0x10, 0x08, 
	0x06, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 
};

#ifdef FONT_USE_SHADER
//
// SHADER
//

// font vertex shader program
static const GLchar * const sFontVertexShader =
	"#version 130\n"
	"\n"
	"uniform mat4 modelviewproj;\n"
	"\n"
	"in vec3 position;\n"
	"in vec4 color;\n"
	"in vec2 texcoord;\n"
	"\n"
	"out vec4 vscolor;\n"
	"out vec2 vstexcoord;\n"
	"\n"
	"void main()\n"
	"{\n"
	"    gl_Position = modelviewproj * vec4(position, 1.0);\n"
	"    vscolor = color;\n"
	"    vstexcoord = texcoord;\n"
	"}\n";

// font fragment shader program
static const GLchar * const sFontFragmentShader =
	"#version 130\n"
	"\n"
	"uniform sampler2D sampler;\n"
	"\n"
	"in vec4 vscolor;\n"
	"in vec2 vstexcoord;\n"
	"\n"
	"out vec4 fragmentcolor;\n"
	"\n"
	"void main(void)\n"
	"{\n"
	"    fragmentcolor = vscolor;\n"
	"    fragmentcolor.a *= texture(sampler, vstexcoord).a;\n"
	"}\n";

// font shader program
static GLuint sFontProgramId;
static GLuint sFontVertexId;
static GLuint sFontFragmentId;

// uniform locations
static GLint sUniformModelViewProj;

// attribute locations
static GLint sAttribPosition;
static GLint sAttribNormal;
static GLint sAttribColor;
static GLint sAttribTexCoord;
#endif

static void InitFontProgram(void)
{
#ifdef FONT_USE_SHADER
	// create font shader
	sFontVertexId = CreateVertexShader(sFontVertexShader);
	sFontFragmentId = CreateFragmentShader(sFontFragmentShader);
	sFontProgramId = CreateProgram(sFontVertexId, sFontFragmentId);

	// get uniform location
	sUniformModelViewProj = glGetUniformLocation(sFontProgramId, "modelviewproj");

	// get attribute locations
	sAttribPosition = glGetAttribLocation(sFontProgramId, "position");
	sAttribColor = glGetAttribLocation(sFontProgramId, "color");
	sAttribTexCoord = glGetAttribLocation(sFontProgramId, "texcoord");
#endif
}

static void CleanupFontProgram(void)
{
#ifdef FONT_USE_SHADER
	DeleteProgram(sFontProgramId);
	sFontProgramId = 0;
	DeleteShader(sFontFragmentId);
	sFontFragmentId = 0;
	DeleteShader(sFontVertexId);
	sFontVertexId = 0;
#endif
}

void InitFonts()
{
	// initialize font shader program
	// (this should be part of a font system initialization...)
	InitFontProgram();

	// generate a texture handle
	glGenTextures(1, &sDefaultFontHandle);

	// register the handle
	Database::texture.Put(aDefaultFontId, sDefaultFontHandle);

	// get a texture template
	TextureTemplate &texture = Database::texturetemplate.Open(sDefaultFontHandle);

	// fill in values
	texture.mInternalFormat = GL_ALPHA8;
	texture.mWidth = aTextureWidth;
	texture.mHeight = aTextureHeight;
	texture.mFormat = GL_ALPHA;
	texture.mMinFilter = GL_NEAREST;
	texture.mMagFilter = GL_NEAREST;
	texture.mWrapS = GL_CLAMP_TO_EDGE;
	texture.mWrapT = GL_CLAMP_TO_EDGE;

	// allocate space
	texture.Allocate(aTextureComponents);

	// unpack font data
	const unsigned char * src = aTextureData;
	unsigned char * dst = texture.mPixels;
	for (unsigned int i = 0; i < sizeof(aTextureData); ++i)
	{
		register unsigned char s = *src++;
		*dst++ = -(s & 1); s >>= 1;
		*dst++ = -(s & 1); s >>= 1;
		*dst++ = -(s & 1); s >>= 1;
		*dst++ = -(s & 1); s >>= 1;
		*dst++ = -(s & 1); s >>= 1;
		*dst++ = -(s & 1); s >>= 1;
		*dst++ = -(s & 1); s >>= 1;
		*dst++ = -(s & 1);
	}

	// instantiate the texture
	InstantiateTexture(sDefaultFontHandle, texture);

	// done with texture template
	Database::texturetemplate.Close(sDefaultFontHandle);

	// generate texture coordinates
	const int aCharacterWidth = 8;
	const int aCharacterHeight = 8;
	const int aCharacterPerRow = aTextureWidth / aCharacterWidth;
	for (int c = 0; c < LAST_CHARACTER-FIRST_CHARACTER+1; ++c)
	{
		Rect<float> &uv = sDefaultFontUVs[c];
		int row = c / aCharacterPerRow;
		int col = c - row * aCharacterPerRow;
		uv.x = float(col * aCharacterWidth) / float(aTextureWidth);
		uv.y = float((row + 1) * aCharacterHeight) / float(aTextureHeight);
		uv.w = float(aCharacterWidth) / float(aTextureWidth);
		uv.h = float(-aCharacterHeight) / float(aTextureHeight);
	}
}

void PreResetFonts(void)
{
	CleanupFontProgram();
}

void PostResetFonts(void)
{
	InitFontProgram();
}

void CleanupFonts(void)
{
	CleanupFontProgram();

	// clear default font handle
	// (texture system releases the font texture)
	sDefaultFontHandle = 0;
}


int FontGetWidth(GLuint handle, int c)
{
	// TO DO: read from font info
	return 8;
}

int FontGetHeight(GLuint handle)
{
	// TO DO: read from font info
	return 8;
}

struct FontVertex
{
	Vector3 pos;
#ifdef FONT_FLOAT_COLOR
	Color4 color;
#else
	unsigned int color;
#endif
	Vector2 texcoord;
};
#ifdef FONT_FLOAT_COLOR
static Color4 sColor;
#else
static unsigned int sColor;
#endif
static size_t sVertexBase;

void FontDrawBegin(GLuint handle)
{
	BindTexture(handle);
#ifdef FONT_USE_SHADER
	if (UseProgram(sFontProgramId) || &GetBoundVertexBuffer() != &GetDynamicVertexBuffer() || ViewProjChanged())
	{
		SetUniformMatrix4(sUniformModelViewProj, ViewProjGet());
	}
	SetAttribFormat(sAttribPosition, 3, GL_FLOAT);
	SetAttribFormat(sAttribColor, 4, GL_UNSIGNED_BYTE);
	SetAttribFormat(sAttribTexCoord, 2, GL_FLOAT);
	SetWorkFormat((1<<sAttribPosition)|(1<<sAttribColor)|(1<<sAttribTexCoord));
#else
	UseProgram(0);
	SetUniformMatrix4(GL_PROJECTION, ProjectionGet());
	SetUniformMatrix4(GL_MODELVIEW, StackGet());
	SetAttribFormat(0, 3, GL_FLOAT);
	SetAttribFormat(2, 4, GL_UNSIGNED_BYTE);
	SetAttribFormat(3, 2, GL_FLOAT);
	SetWorkFormat((1<<0)|(1<<2)|(1<<3));
#endif
	SetDrawMode(GL_TRIANGLES);
	sVertexBase = GetVertexCount();
}

void FontDrawEnd()
{
	IndexQuads(sVertexBase, GetVertexCount() - sVertexBase);
	BindTexture(0);
}

void FontDrawColor(const Color4 &color)
{
#ifdef FONT_FLOAT_COLOR
	sColor = color;
#else
	sColor = 
		GLubyte(Clamp(xs_RoundToInt(color.r * 255), 0, 255)) |
		GLubyte(Clamp(xs_RoundToInt(color.g * 255), 0, 255)) << 8 |
		GLubyte(Clamp(xs_RoundToInt(color.b * 255), 0, 255)) << 16 |
		GLubyte(Clamp(xs_RoundToInt(color.a * 255), 0, 255)) << 24;
#endif
}

static FontVertex *FontDrawCharacterInternal(FontVertex *v, int c, float x, float y, float w, float h, float z)
{
	// texture coordinates
	const Rect<float> &uv = sDefaultFontUVs[c - FIRST_CHARACTER];

	// submit vertex data
	v->pos = Vector3(x, y, z);
	v->color = sColor;
	v->texcoord = Vector2(uv.x , uv.y);
	++v;
	v->pos = Vector3(x + w, y, z);
	v->color = sColor;
	v->texcoord = Vector2(uv.x + uv.w, uv.y);
	++v;
	v->pos = Vector3(x + w, y + h, z);
	v->color = sColor;
	v->texcoord = Vector2(uv.x + uv.w, uv.y + uv.h);
	++v;
	v->pos = Vector3(x, y + h, z);
	v->color = sColor;
	v->texcoord = Vector2(uv.x , uv.y + uv.h);
	++v;
	return v;
}

void FontDrawCharacter(int c, float x, float y, float w, float h, float z)
{
	register FontVertex * __restrict v = static_cast<FontVertex *>(AllocVertices(4));
	FontDrawCharacterInternal(v, c, x, y, w, h, z);
}

void FontDrawString(const char *s, float x, float y, float w, float h, float z, float wrap)
{
	float x0 = x;

	register FontVertex * __restrict v = static_cast<FontVertex *>(AllocVertices(4 * strlen(s)));

	while (*s)
	{
		v = FontDrawCharacterInternal(v, *s, x, y, w, h, z);

		s++;
		x += w;

		if (x >= wrap)
		{
			x = x0;
			y += h;
		}
	}
}
